package com.hang.jvm.risc.v2_memory_bus;

import com.hang.jvm.risc.v2_memory_bus.exception.LoadAccessFaultException;

import java.util.List;

import static com.hang.jvm.risc.v2_memory_bus.Param.DRAM_BASE;
import static com.hang.jvm.risc.v2_memory_bus.Param.DRAM_SIZE;

public class Dram {

    private byte[] dram;

    private List<Integer> sizeList = List.of(8, 16, 32, 64);

    public Dram(byte[] code) {
        this.dram = new byte[(int) DRAM_SIZE];
        System.arraycopy(code, 0, dram, 0, code.length);
    }

    /**
     * 从内存中读取数据
     *
     * @param addr 地址
     * @param size 位数，只能是 8、16、32、64
     * @return 读取到的数据
     * @throws LoadAccessFaultException 读取异常
     */
    public long load(long addr, int size) throws LoadAccessFaultException {
        if (!sizeList.contains(size)) {
            throw new LoadAccessFaultException(addr);
        }

        // 需要读取的字节数
        int byteCount = size / 8;
        // pc 在初始化时，已经赋予 DRAM_BASE ，程序是从 DRAM_BASE 开始执行，换算成实际地址
        int index = (int) (addr - DRAM_BASE);
        // 读取第一个字节，小端
        long code = dram[index] & 0xFFL;
        // 读取余下的字节
        for (int i = 1; i < byteCount; i++) {
            long next = (dram[index + i] & 0xFFL) << (i * 8);
            code = code | next;
        }
        return code;
    }

    /**
     * 存储数据到内存
     *
     * @param addr  地址
     * @param size  位数，只能是 8、16、32、64
     * @param value 要存储的数据
     */
    public void store(long addr, int size, long value) throws LoadAccessFaultException {
        if (!sizeList.contains(size)) {
            throw new LoadAccessFaultException(addr);
        }

        // 需要存储的字节数
        int byteCount = size / 8;
        // pc 在初始化时，已经赋予 DRAM_BASE ，程序是从 DRAM_BASE 开始执行，换算成实际地址
        int index = (int) (addr - DRAM_BASE);
        //
        for (int i = 0; i < byteCount; i++) {
            dram[index + i] = (byte) ((value >>> (i * 8)) & 0xff);
        }

    }

}
